//////////////////////////////////////////////////////////////////////////
///	\file:	w3c.cpp
///	Copyright (MingYou) 2009 - All Rights Reserved
///	Author:	Robert.Ma
///	Date:	[2009/05/13]
///	Description:
///
//////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "w3c.h"

#include <cstring>

#pragma comment(lib, "wininet.lib")


#define __HTTP_VERB_GET	_T("GET")
#define __HTTP_VERB_POST _T("POST")
#define __HTTP_ACCEPT_TYPE _T("*/*")
#define __HTTP_ACCEPT _T("Accept: */*\r\n")

#define __DEFAULT_BUF_SIZE 1024

void __w3cexcept(LPCTSTR szaddress, long lPort, W3Client::w3t t, LPCTSTR szmsg)
{
#ifdef _DEBUG
	CString sztmp;
	sztmp += _T("[ ");

	switch (t)
	{
	case W3Client::w3http:
		sztmp += _T("http://");
		break;
	case W3Client::w3https:
		sztmp += _T("https://");
		break;
	case W3Client::w3ftp:
		sztmp += _T("ftp://");
		break;
	}

	sztmp += szaddress;
	sztmp += _T(":");
	TCHAR szp[10] = _T("\0");
	_stprintf(szp, _T("%d"), lPort);
	sztmp += szp;
	sztmp += _T(" ] ");
	sztmp += szmsg;

	::OutputDebugString(sztmp);

	DWORD err =::GetLastError();
	LPVOID lpMsgBuffer;
	::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
	                GetModuleHandle(_T("wininet.dll")),
	                err,
	                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
	                reinterpret_cast<LPTSTR>(&lpMsgBuffer),
	                0,
	                NULL);

	TCHAR szmsg2[1024] = _T("\0");
	_stprintf(szmsg2, _T("[%d, 0x%08x] %s"), err, err, reinterpret_cast<LPTSTR>(lpMsgBuffer));
	::OutputDebugString(szmsg2);
	::LocalFree(lpMsgBuffer);
#endif
}

void __w3cexcept(LPCTSTR szaddress, long lPort, W3Client::w3t t, LPCTSTR lpstrUri, LPCTSTR szmsg)
{
#ifdef _DEBUG
	CString strTmp;
	strTmp += _T("[ ");

	switch (t)
	{
	case W3Client::w3http:
		strTmp += _T("http://");
		break;
	case W3Client::w3https:
		strTmp += _T("https://");
		break;
	case W3Client::w3ftp:
		strTmp += _T("ftp://");
		break;
	}

	strTmp += szaddress;
	strTmp += _T(":");
	TCHAR szp[10] = _T("\0");
	_stprintf(szp, _T("%d"), lPort);
	strTmp += szp;
	strTmp += lpstrUri;
	strTmp += _T(" ] ");
	strTmp += szmsg;

	::OutputDebugString(strTmp);

	LPVOID lpMsgBuffer;
	DWORD dwError =::GetLastError();
	::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
	                GetModuleHandle(_T("wininet.dll")),
	                dwError,
	                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
	                reinterpret_cast<LPTSTR>(&lpMsgBuffer),
	                0,
	                NULL);
	TCHAR szmsg2[1024] = _T("\0");
	_stprintf(szmsg2, _T("[%d, 0x%08x] %s"), dwError, dwError, reinterpret_cast<LPTSTR>(lpMsgBuffer));
	::OutputDebugString(szmsg2);
	::LocalFree(lpMsgBuffer);
#endif
}

W3Client::__HTTP_ARG::__HTTP_ARG(LPCSTR lpstrName, LPCSTR lpstrValue, bool b/* = false*/)
		: strName(lpstrName), strValue(lpstrValue), bFile(b)
{}

W3Client::__HTTP_ARG::__HTTP_ARG(LPCSTR lpstrName, const int iValue)
		: strName(lpstrName), bFile(false)
{
	strValue.Format("%d", iValue);
}

W3Client::__HTTP_ARG::__HTTP_ARG(LPCSTR lpstrName, const long lValue)
		: strName(lpstrName), bFile(false)
{
	strValue.Format("%d", lValue);
}

W3Client::__HTTP_ARG::__HTTP_ARG(LPCSTR lpstrName, const float fValue)
		: strName(lpstrName), bFile(false)
{
	strValue.Format("%f", fValue);
}

W3Client::__HTTP_ARG::__HTTP_ARG(LPCSTR lpstrName, const double dValue)
		: strName(lpstrName), bFile(false)
{
	strValue.Format("%f", dValue);
}

LPCTSTR W3Client::__HTTP_ARG::type()
{
	static TCHAR szType[1024] = _T("\0");
	long lLen = 1024;
	ULONG ulDot = 0;

	CString strNameW(strName);

	for ( ulDot = strName.GetLength(); ulDot > 0; --ulDot )
	{
		if ( !(_tcsnccmp( (LPCTSTR)(strNameW) + ulDot, _T("."), 1)) )
		{
			break;
		}
	}

	HKEY	hKEY;
	const	TCHAR* szWord = (LPCTSTR)(strNameW) + ulDot;

	if ( ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_CLASSES_ROOT, szWord, 0, KEY_QUERY_VALUE, &hKEY) )
	{
		if ( ERROR_SUCCESS != ::RegQueryValueEx(hKEY, TEXT("Content Type"),
		                                        NULL, NULL,
		                                        (LPBYTE)szType,
		                                        (ULONG*)&lLen) )
		{
			_tcsncpy_s(szType, 1024,
			           _T("application/octet-stream"),
			           _tcslen(_T("application/octet-stream")));
		}

		::RegCloseKey(hKEY);
	}
	else
	{
		_tcsncpy_s(szType, 1024,
		           _T("application/octet-stream"),
		           _tcslen(_T("application/octet-stream")));
	}

	return szType;
}

ULONG W3Client::__HTTP_ARG::length()
{
	return strName.GetLength() + strValue.GetLength() + 1;
}

ULONG W3Client::__HTTP_ARG::dump(BYTE* pBuf, ULONG /*s*/)
{
	::memcpy(pBuf, (LPCSTR)(strName), strName.GetLength());
	::memcpy(pBuf + strName.GetLength(), "=", 1);
	::memcpy(pBuf + strName.GetLength() + 1,
	         (LPCSTR)strValue,
	         strValue.GetLength());

	return strName.GetLength() + strValue.GetLength() + 1;
}

ULONG W3Client::__HTTP_ARG::length2()
{
	ULONG ulLen = 0;

	if ( bFile )
	{
		CString strValueW(strValue);

		HANDLE hFile =::CreateFile(strValueW,
		                           GENERIC_READ, // desired access
		                           FILE_SHARE_READ, // share mode
		                           NULL, // security attribute
		                           OPEN_EXISTING, // create disposition
		                           FILE_ATTRIBUTE_NORMAL, // flag and attributes
		                           NULL); // template file handle

		ulLen = __SIZE_HTTP_HEAD_LINE * 3 +::GetFileSize(hFile, NULL);
		::CloseHandle(hFile);
	}
	else
	{
		ulLen = __SIZE_HTTP_HEAD_LINE * 4;
	}

	return ulLen;
}

ULONG W3Client::__HTTP_ARG::dump2(BYTE* pBuf,
                                  ULONG /*ulSize*/,
                                  LPCSTR lpstrBoundry/* = __DEFAULT_BOUNDRY_TAG*/)
{
	ULONG ulLength = 0;

	if ( bFile ) // file
	{
		sprintf(reinterpret_cast<char*>(pBuf),
		        "--%s\r\n"
		        "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n"
		        "\r\n",
		        lpstrBoundry, strName, strValue);
		ulLength = 2 + ::strlen(lpstrBoundry)
		           + 2 + 38 + strName.GetLength()
		           + 13 + strValue.GetLength() + 5;

		CString strValueW(strValue);

		HANDLE hFile =::CreateFile(strValueW,
		                           GENERIC_READ, // desired access
		                           FILE_SHARE_READ, // share mode
		                           NULL, // security attribute
		                           OPEN_EXISTING, // create disposition
		                           FILE_ATTRIBUTE_NORMAL, // flag and attributes
		                           NULL); // template file handle

		if ( NULL != hFile && INVALID_HANDLE_VALUE != hFile )
		{
			ULONG ulFileSize =::GetFileSize(hFile, NULL);
			BYTE* pbuf = pBuf + ulLength;
			ULONG ulRead = 0;
			ULONG ulT = 0;

			while ( ::ReadFile(hFile, pbuf + ulT, 1024, &ulRead, NULL)
			        && ulRead > 0 && ulT <= ulFileSize )
			{
				ulT += ulRead;
			}

			::memcpy(&(pBuf[ulLength+ulT]), "\r\n", 2);
			::CloseHandle(hFile);

			ulLength += ulT;
			ulLength += 2;
		}
	}
	else  // general form data
	{
		sprintf(reinterpret_cast<char*>(pBuf),
		        "--%s\r\n"
		        "Content-Disposition: form-data; name=\"%s\"\r\n"
		        "\r\n"
		        "%s\r\n",
		        lpstrBoundry, strName, strValue);
		ulLength = 2 + strlen(lpstrBoundry)
		           + 2 + 38 + strName.GetLength()
		           + 5 + strValue.GetLength() + 2;
	}

	return ulLength;
}

W3Client::__HTTP_COOKIE::__HTTP_COOKIE(const __HTTP_COOKIE& c)
		: strName(c.strName), strValue(c.strValue) {}

W3Client::__HTTP_COOKIE::__HTTP_COOKIE(LPCTSTR lpstrName, const TCHAR* szv)
		: strName(lpstrName), strValue(szv) {}

W3Client::__HTTP_COOKIE::__HTTP_COOKIE(LPCTSTR lpstrName, const int iValue)
		: strName(lpstrName)
{
	strValue.Format(_T("%d"), iValue);
}

W3Client::__HTTP_COOKIE::__HTTP_COOKIE(LPCTSTR lpstrName, const long lValue)
		: strName(lpstrName)
{
	strValue.Format(_T("%d"), lValue);
}

W3Client::__HTTP_COOKIE::__HTTP_COOKIE(LPCTSTR lpstrName, const float fValue)
		: strName(lpstrName)
{
	strValue.Format(_T("%f"), fValue);
}

W3Client::__HTTP_COOKIE::__HTTP_COOKIE(LPCTSTR lpstrName, const double dValue)
		: strName(lpstrName)
{
	strValue.Format(_T("%f"), dValue);
}

W3Client::W3Client()
		: m_hOpen(NULL),
		m_hConnection(NULL),
		m_hRequest(NULL)
{}

W3Client::~W3Client()
{
	InitializePostArguments();
	InitializeCookies();
}

LPCTSTR W3Client::GetURI() const
{
	return m_strUri;
}

#pragma warning ( disable : 4701 )

// W3Client : synchronized www client
bool W3Client::Connect(LPCTSTR lpstrAddress,
                       LPCTSTR lpstrUser /*=NULL*/,
                       LPCTSTR lpstrPassword /*=NULL*/,
                       LPCTSTR lpstrAgent /*=__W3_DEFAULT_AGENT*/,
                       LPCTSTR lpProxy/* = NULL*/)
{
	TCHAR szp[__DEFAULT_BUF_SIZE] = _T("\0"), szus[__DEFAULT_BUF_SIZE] = _T("\0");
	TCHAR szpw[__DEFAULT_BUF_SIZE] = _T("\0");
	TCHAR sza[__DEFAULT_BUF_SIZE] = _T("\0"), szuri[__DEFAULT_BUF_SIZE] = _T("\0");
	WORD wPort = 0;

	__w3curlparse(lpstrAddress,
	              szp, __DEFAULT_BUF_SIZE,
	              szus, __DEFAULT_BUF_SIZE,
	              szpw, __DEFAULT_BUF_SIZE,
	              sza, __DEFAULT_BUF_SIZE,
	              szuri, __DEFAULT_BUF_SIZE,
	              wPort);

	w3t wt;

	if (!_tcsnccmp(szp, _T("https"), 5))
	{
		wt = w3https;
	}
	else if (!_tcsnccmp(szp, _T("http"), 4))
	{
		wt = w3http;
	}
	else if (!_tcsnccmp(szp, _T("ftp"), 3))
	{
		wt = w3ftp;
	}

	m_strUri = szuri;

	return Connect(sza, wPort,
	               ((lpstrUser || (!lpstrUser && _tcslen(szus) == 0)) ? lpstrUser : szus),
	               ((lpstrPassword || (!lpstrPassword && _tcslen(szpw) == 0)) ? lpstrPassword : szpw),
	               wt, lpstrAgent,
	               lpProxy);
}

#pragma warning ( default : 4701 )

bool W3Client::Connect(LPCTSTR lpstrAddress,
                       INTERNET_PORT wPort,
                       LPCTSTR lpstrUser /*=NULL*/,
                       LPCTSTR lpstrPassword /*=NULL*/,
                       w3t eType /*=w3http*/,
                       LPCTSTR lpstrAgent /*=__W3_DEFAULT_AGENT*/,
                       LPCTSTR lpProxy/* = NULL*/)
{
	bool bResult = true;

	try
	{
		m_hOpen =::InternetOpen(lpstrAgent, // agent
		                        INTERNET_OPEN_TYPE_PROXY, // access type
		                        lpProxy, // proxy
		                        NULL, // proxy by pass
		                        0); // flags

		if ( !m_hOpen || m_hOpen == INVALID_HANDLE_VALUE )
		{
			throw _T("open internet failed...");
		}

		switch ( eType )
		{
		case w3ftp:
			m_hConnection =::InternetConnect(m_hOpen,
			                                 lpstrAddress, (INTERNET_PORT)(wPort ? wPort : INTERNET_DEFAULT_FTP_PORT),
			                                 lpstrUser, lpstrPassword,
			                                 INTERNET_SERVICE_FTP,
			                                 INTERNET_FLAG_PASSIVE,
			                                 NULL);
			break;
		case w3http:
			m_hConnection =::InternetConnect(m_hOpen,
			                                 lpstrAddress, (INTERNET_PORT)(wPort ? wPort : INTERNET_DEFAULT_HTTP_PORT),
			                                 lpstrUser, lpstrPassword,
			                                 INTERNET_SERVICE_HTTP,
			                                 0,
			                                 NULL);
			break;
		case w3https:
			m_hConnection =::InternetConnect(m_hOpen,
			                                 lpstrAddress, (INTERNET_PORT)(wPort ? wPort : INTERNET_DEFAULT_HTTPS_PORT),
			                                 lpstrUser, lpstrPassword,
			                                 INTERNET_SERVICE_HTTP,
			                                 0,
			                                 NULL);
			break;
		}

		if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
		{
			throw _T("connect failed...");
		}

		if ( ::InternetAttemptConnect(NULL) != ERROR_SUCCESS )
		{
			throw _T("connect failed...");
		}

		m_strAddress = lpstrAddress;

		if ( !wPort )
		{
			switch ( eType )
			{
			case w3ftp:
				m_wPort = INTERNET_DEFAULT_FTP_PORT;
				break;
			case w3http:
				m_wPort = INTERNET_DEFAULT_HTTP_PORT;
				break;
			case w3https:
				m_wPort = INTERNET_DEFAULT_HTTPS_PORT;
				break;
			}
		}
		else
		{
			m_wPort = wPort;
		}

		m_eType = eType;

		if ( lpstrUser )
		{
			m_strUser = lpstrUser;
		}

		if ( lpstrPassword )
		{
			m_strPassword = lpstrPassword;
		}

		InitializeCookies();
		InitializePostArguments();
	}
	catch ( LPCTSTR szm )
	{
		bResult = false;

		if ( m_hOpen || m_hOpen != INVALID_HANDLE_VALUE )
		{
			::InternetCloseHandle(m_hOpen);
		}

		if ( m_hConnection || m_hConnection != INVALID_HANDLE_VALUE )
		{
			::CloseHandle(m_hConnection);
		}

		__w3cexcept(lpstrAddress, wPort, eType, szm);
	}
	catch ( ... )
	{
		bResult = false;

		if ( m_hOpen || m_hOpen != INVALID_HANDLE_VALUE )
		{
			::InternetCloseHandle(m_hOpen);
		}

		if ( m_hConnection || m_hConnection != INVALID_HANDLE_VALUE )
		{
			::CloseHandle(m_hConnection);
		}

		__w3cexcept(lpstrAddress, wPort, eType, _T("unknown exception..."));
	}

	return bResult;
}

void W3Client::Close()
{
	if ( m_hRequest )
	{
		::InternetCloseHandle(m_hRequest);
	}

	if ( m_hConnection )
	{
		::InternetCloseHandle(m_hConnection);
	}

	if ( m_hOpen )
	{
		::InternetCloseHandle(m_hOpen);
	}

	return;
}

bool W3Client::Request(LPCTSTR lpstrUri, w3m m, LPCTSTR lpstrRef /*=NULL*/)
{
	bool bflag = false;

	try
	{
		switch ( m )
		{
		case reqPost:
			bflag = RequestPost(lpstrUri, lpstrRef);
			break;
		case reqPostMultipartsFormdata:
			bflag = RequestPost2(lpstrUri, lpstrRef);
			break;
		case reqGet:
			bflag = RequestGet(lpstrUri, lpstrRef);
			break;
		}

		if ( bflag )
		{
			m_strUri = lpstrUri;
		}
		else
		{
			::InternetCloseHandle(m_hRequest);
			m_hRequest = NULL;
		}

	}
	catch ( LPCTSTR szm )
	{
		::InternetCloseHandle(m_hRequest);
		m_hRequest = NULL;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, szm);
	}
	catch ( ... )
	{
		::InternetCloseHandle(m_hRequest);
		m_hRequest = NULL;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, _T("unknown exception..."));
	}

	return bflag;
}

bool W3Client::RequestGet(LPCTSTR lpstrUri, LPCTSTR lpstrRef /*=NULL*/)
{
	static LPCTSTR szAcceptType = __HTTP_ACCEPT_TYPE;

	if (!m_hConnection || m_hConnection == INVALID_HANDLE_VALUE)
	{
		throw _T("handle not opened...");
	}

	m_hRequest =::HttpOpenRequest( m_hConnection,
	                               __HTTP_VERB_GET, // HTTP Verb
	                               lpstrUri, // Object Name
	                               HTTP_VERSION, // Version
	                               lpstrRef, // Reference
	                               &szAcceptType, // Accept Type
	                               INTERNET_FLAG_NO_CACHE_WRITE | (m_eType == w3https ? INTERNET_FLAG_SECURE : 0),
	                               NULL); // context call-back point

	if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
	{
		throw _T("request failed...");
	}

	// REPLACE HEADER
	if ( !::HttpAddRequestHeaders(m_hRequest,
	                              __HTTP_ACCEPT, _tcslen(__HTTP_ACCEPT),
	                              HTTP_ADDREQ_FLAG_REPLACE) )
	{
		throw _T("additional header failed...");
	}

	// COOKIE ADD
	if ( m_listCookies.size() > 0 )
	{
		CString strUrl;

		switch (m_eType)
		{
		case w3http:
			strUrl = _T("http://");
			break;
		case w3https:
			strUrl = _T("https://");
			break;
		}

		strUrl += m_strAddress;

		if ( !((m_eType == w3http && m_wPort == INTERNET_DEFAULT_HTTP_PORT)
		        || (m_eType == w3https && m_wPort == INTERNET_DEFAULT_HTTPS_PORT)) )
		{
			TCHAR tmp[10] = _T("\0");
			_stprintf(tmp, _T(":%d"), m_wPort);
			strUrl += tmp;
		}

		strUrl += lpstrUri;

		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin(); it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* pc = reinterpret_cast<HTTP_COOKIE*>(*it);

			if ( !::InternetSetCookie(strUrl, pc->strName, pc->strValue) )
			{
				throw _T("add cookie failed...");
			}
		}
	}

	// SEND REQUEST
	if ( !::HttpSendRequest(m_hRequest,	// handle by returned HttpOpenRequest
	                        NULL, // additional HTTP header
	                        0, // additional HTTP header length
	                        NULL, // additional data in HTTP Post or HTTP Put
	                        0) ) // additional data length
	{
		throw _T("request failed...");
	}

	return true;
}

void W3Client::InitializePostArguments()
{
	if ( m_listArgs.size() > 0 )
	{
		for ( std::list<HTTP_ARG*>::iterator it = m_listArgs.begin(); it != m_listArgs.end(); ++it )
		{
			HTTP_ARG* p = reinterpret_cast<HTTP_ARG*>(*it);
			delete p;
		}

		m_listArgs.clear();
	}
}

void W3Client::AddPostArgument(LPCSTR lpstrName, const int iValue)
{
	HTTP_ARG* pa = new HTTP_ARG(lpstrName, iValue);
	m_listArgs.push_back(pa);
}

void W3Client::AddPostArgument(LPCSTR lpstrName, const long lValue)
{
	HTTP_ARG* pa = new HTTP_ARG(lpstrName, lValue);
	m_listArgs.push_back(pa);
}

void W3Client::AddPostArgument(LPCSTR lpstrName, const float fValue)
{
	HTTP_ARG* pa = new HTTP_ARG(lpstrName, fValue);
	m_listArgs.push_back(pa);
}

void W3Client::AddPostArgument(LPCSTR lpstrName, const double dValue)
{
	HTTP_ARG* pa = new HTTP_ARG(lpstrName, dValue);
	m_listArgs.push_back(pa);
}

void W3Client::AddPostArgument(LPCSTR lpstrName, LPCSTR lpstrValue, bool bfile /*=false*/)
{
	HTTP_ARG* pa = new HTTP_ARG(lpstrName, lpstrValue, bfile);
	m_listArgs.push_back(pa);
}

void W3Client::InitializeCookies()
{
	if ( m_listCookies.size() > 0 )
	{
		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin(); it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* p = reinterpret_cast<HTTP_COOKIE*>(*it);
			delete p;
		}

		m_listCookies.clear();
	}
}

void W3Client::AddCookie(LPCTSTR lpstrName, LPCTSTR lpstrValue)
{
	HTTP_COOKIE* pc = new HTTP_COOKIE(lpstrName, lpstrValue);
	m_listCookies.push_back(pc);
}

void W3Client::AddCookie(LPCTSTR lpstrName, const int iValue)
{
	HTTP_COOKIE* pc = new HTTP_COOKIE(lpstrName, iValue);
	m_listCookies.push_back(pc);
}

void W3Client::AddCookie(LPCTSTR lpstrName, const long lValue)
{
	HTTP_COOKIE* pc = new HTTP_COOKIE(lpstrName, lValue);
	m_listCookies.push_back(pc);
}

void W3Client::AddCookie(LPCTSTR lpstrName, const float fValue)
{
	HTTP_COOKIE* pc = new HTTP_COOKIE(lpstrName, fValue);
	m_listCookies.push_back(pc);
}

void W3Client::AddCookie(LPCTSTR lpstrName, const double dValue)
{
	HTTP_COOKIE* pc = new HTTP_COOKIE(lpstrName, dValue);
	m_listCookies.push_back(pc);
}


ULONG W3Client::GetPostArgumentsLength()
{
	ULONG ulLen = 0;

	if ( m_listArgs.size() > 0 )
	{
		for ( std::list<HTTP_ARG*>::iterator it = m_listArgs.begin(); it != m_listArgs.end(); ++it )
		{
			HTTP_ARG* p = reinterpret_cast<HTTP_ARG*>(*it);
			ulLen += p->length() + 1;
		}
	}

	return ulLen ? ulLen - 1 : 0;
}

ULONG W3Client::GetPostData(BYTE* pBuf, ULONG ulLen)
{
	ULONG lCpy = 0;

	if (ulLen > GetPostArgumentsLength() && m_listArgs.size() > 0)
	{
		for ( std::list<HTTP_ARG*>::iterator it = m_listArgs.begin(); it != m_listArgs.end(); ++it )
		{
			HTTP_ARG* p = reinterpret_cast<HTTP_ARG*>(*it);
			p->dump(pBuf + lCpy, p->length());
			pBuf[lCpy+p->length()] = _T('&');
			lCpy += p->length() + 1;
		}

		pBuf[lCpy-1] = _T('\0');
	}

	return lCpy ? lCpy - 1 : 0;
}

bool W3Client::RequestPost(LPCTSTR lpstrUri, LPCTSTR lpstrRef /*=NULL*/)
{
	static LPCTSTR szAcceptType = __HTTP_ACCEPT_TYPE;
	static LPCTSTR szContentType = _T("Content-Type: application/x-www-form-urlencoded\r\n");

	BYTE* pBuf = NULL;
	ULONG ulLen = 0;

	if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
	{
		throw _T("handle not opened...");
	}


	m_hRequest =::HttpOpenRequest(m_hConnection,
	                              __HTTP_VERB_POST, // HTTP Verb
	                              lpstrUri, // Object Name
	                              HTTP_VERSION, // Version
	                              lpstrRef, // Reference
	                              &szAcceptType, // Accept Type
	                              INTERNET_FLAG_KEEP_CONNECTION |
	                              INTERNET_FLAG_NO_CACHE_WRITE |
	                              INTERNET_FLAG_FORMS_SUBMIT |
	                              (m_eType == w3https ? INTERNET_FLAG_SECURE : 0),
	                              NULL); // context call-back point

	if ( !m_hRequest || INVALID_HANDLE_VALUE == m_hRequest )
	{
		throw _T("request failed...");
	}

	// REPLACE HEADER
	if ( !::HttpAddRequestHeaders(m_hRequest, __HTTP_ACCEPT, _tcslen(__HTTP_ACCEPT), HTTP_ADDREQ_FLAG_REPLACE) )
	{
		throw _T("additional header failed...");
	}

	// COOKIE ADD
	if ( m_listCookies.size() > 0 )
	{
		CString strUrl;

		switch (m_eType)
		{
		case w3http:
			strUrl = _T("http://");
			break;
		case w3https:
			strUrl = _T("https://");
			break;
		}

		strUrl += m_strAddress;

		if ( !( ( w3http == m_eType && INTERNET_DEFAULT_HTTP_PORT == m_wPort )
		        || ( w3https == m_eType && INTERNET_DEFAULT_HTTPS_PORT == m_wPort ) ) )
		{
			TCHAR tmp[10] = _T("\0");
			_stprintf(tmp, _T(":%d"), m_wPort);
			strUrl += tmp;
		}

		strUrl += lpstrUri;

		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin();
			  it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* pc = reinterpret_cast<HTTP_COOKIE*>(*it);

			if ( !::InternetSetCookie(strUrl, pc->strName, pc->strValue) )
			{
				throw _T("add cookie failed...");
			}
		}
	}

	// GET POST ARGUMENTS
	pBuf = reinterpret_cast<BYTE*>(::HeapAlloc(::GetProcessHeap(),
	                               HEAP_ZERO_MEMORY,
	                               GetPostArgumentsLength() + 1));
	ulLen = GetPostData(pBuf, GetPostArgumentsLength() + 1);

	// SEND REQUEST WITH POST ARGUEMENTS
	if ( !::HttpSendRequest(m_hRequest,	// handle by returned HttpOpenRequest
	                        szContentType, // additional HTTP header
	                        _tcslen(szContentType), // additional HTTP header length
	                        reinterpret_cast<LPVOID>(pBuf), // additional data in HTTP Post or HTTP Put
	                        ulLen ) // additional data length
	        && ::GetLastError() != 12168)
	{
		::HeapFree(::GetProcessHeap(), 0, pBuf);
		throw _T("request failed...");
	}

	::HeapFree(::GetProcessHeap(), 0, pBuf);

	return true;
}

ULONG W3Client::GetMultiPartsFormDataLength()
{
	ULONG ulLen = 0;

	if ( m_listArgs.size() > 0 )
	{
		for ( std::list<HTTP_ARG*>::iterator it = m_listArgs.begin(); it != m_listArgs.end(); ++it )
		{
			HTTP_ARG* p = reinterpret_cast<HTTP_ARG*>(*it);
			ulLen += p->length2();
		}
	}

	return ulLen;
}

ULONG W3Client::AllocMultiPartsFormData(BYTE*& prBuf, LPCSTR lpstrPoundry)
{
	ULONG ulLen = 0;
	ULONG ulNS = GetMultiPartsFormDataLength() + 1;

	if ( NULL != prBuf )
	{
		return 0;
	}

	prBuf = reinterpret_cast<BYTE*>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, ulNS));

	if ( m_listArgs.size() > 0 )
	{
		for ( std::list<HTTP_ARG*>::iterator it = m_listArgs.begin(); it != m_listArgs.end(); ++it )
		{
			HTTP_ARG* pa = reinterpret_cast<HTTP_ARG*>(*it);
			ulLen += pa->dump2(prBuf + ulLen, ulNS - ulLen, lpstrPoundry);
		}
	}

	::memcpy(prBuf + ulLen, "--", 2);
	::memcpy(prBuf + ulLen + 2, lpstrPoundry, strlen(lpstrPoundry));
	::memcpy(prBuf + ulLen + 2 + strlen(lpstrPoundry), "--\r\n", 4);

	ulLen += 2 + strlen(lpstrPoundry) + 4;

	return ulLen;
}

void W3Client::FreeMultiPartsFormData(BYTE* pBuf)
{
	::HeapFree(::GetProcessHeap(), 0, pBuf);
}

bool W3Client::RequestPost2(LPCTSTR lpstrUri, LPCTSTR lpstrRef /*=NULL*/)
{
	static LPCTSTR lpstrAcceptType = __HTTP_ACCEPT_TYPE;
	static LPCTSTR lpstrContentType = _T("Content-Type: multipart/form-data; boundary=--MULTI-PARTS-FORM-DATA-BOUNDARY\r\n");

	BYTE* pBuf = NULL;
	ULONG ulLen = 0;

	if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
	{
		throw _T("handle not opened...");
	}


	m_hRequest =::HttpOpenRequest(m_hConnection,
	                              __HTTP_VERB_POST, // HTTP Verb
	                              lpstrUri, // Object Name
	                              HTTP_VERSION, // Version
	                              lpstrRef, // Reference
	                              &lpstrAcceptType, // Accept Type
	                              INTERNET_FLAG_KEEP_CONNECTION |
	                              INTERNET_FLAG_NO_CACHE_WRITE |
	                              INTERNET_FLAG_FORMS_SUBMIT |
	                              (m_eType == w3https ? INTERNET_FLAG_SECURE : 0),
	                              NULL); // context call-back point

	if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
	{
		throw _T("request failed...");
	}

	// REPLACE HEADER
	if ( !::HttpAddRequestHeaders(m_hRequest,
	                              __HTTP_ACCEPT,
	                              _tcslen(__HTTP_ACCEPT),
	                              HTTP_ADDREQ_FLAG_REPLACE) )
	{
		throw _T("additional header failed...");
	}

	if (!::HttpAddRequestHeaders(m_hRequest,
	                             lpstrContentType,
	                             _tcslen(lpstrContentType),
	                             HTTP_ADDREQ_FLAG_ADD_IF_NEW))
	{
		throw _T("additional header failed...");
	}

	// COOKIE ADD
	if ( m_listCookies.size() > 0 )
	{
		CString strUrl;

		switch (m_eType)
		{
		case w3http:
			strUrl = _T("http://");
			break;
		case w3https:
			strUrl = _T("https://");
			break;
		}

		strUrl += m_strAddress;

		if ( !( (m_eType == w3http && m_wPort == INTERNET_DEFAULT_HTTP_PORT) ||
		        (m_eType == w3https && m_wPort == INTERNET_DEFAULT_HTTPS_PORT) ) )
		{
			TCHAR tmp[10] = _T("\0");
			_stprintf(tmp, _T(":%d"), m_wPort);
			strUrl += tmp;
		}

		strUrl += lpstrUri;

		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin(); it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* pc = reinterpret_cast<HTTP_COOKIE*>(*it);

			if (!::InternetSetCookie(strUrl, pc->strName, pc->strValue))
			{
				throw _T("add cookie failed...");
			}
		}
	}

	// build multi-parts/form-data
	ulLen = AllocMultiPartsFormData(pBuf, "--MULTI-PARTS-FORM-DATA-BOUNDARY");

	// ADD HEADER CONTENT LENGTH
	TCHAR szcl[__DEFAULT_BUF_SIZE] = _T("\0");
	_stprintf(szcl, _T("Content-Length: %d\r\n"), ulLen);

	if ( !::HttpAddRequestHeaders( m_hRequest, szcl, _tcslen(szcl), HTTP_ADDREQ_FLAG_ADD_IF_NEW) )
	{
		throw _T("additional header failed...");
	}

	// SEND REQUEST WITH HttpSendRequestEx and InternetWriteFile
	static INTERNET_BUFFERS InternetBufferIn = {0};
	InternetBufferIn.dwStructSize = sizeof(INTERNET_BUFFERS);
	InternetBufferIn.Next = NULL;

	if ( !::HttpSendRequestEx(m_hRequest, &InternetBufferIn, NULL, HSR_INITIATE, 0) )
	{
		// free
		FreeMultiPartsFormData(pBuf);
		throw _T("request failed");
	}

	ULONG ulOut = 0;

	if ( !::InternetWriteFile(m_hRequest, pBuf, ulLen, &ulOut) )
	{
		// free
		FreeMultiPartsFormData(pBuf);
		throw _T("request failed");
	}

	if ( !::HttpEndRequest(m_hRequest, NULL, HSR_INITIATE, 0) )
	{
		// free
		FreeMultiPartsFormData(pBuf);
		throw _T("request failed");
	}

	// free multi-parts/form-data
	FreeMultiPartsFormData(pBuf);

	return true;
}

ULONG W3Client::QueryCookie(BYTE* pBuf, ULONG ulLen, ULONG ulIdx /*=0*/)
{
	if ( !::HttpQueryInfo(m_hRequest, HTTP_QUERY_SET_COOKIE, pBuf, &ulLen, &ulIdx) )
	{
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, _T("query cookie failed..."));

		return 0;
	}

	return ulLen;
}

ULONG W3Client::QueryContentLength()
{
	TCHAR szt[__DEFAULT_BUF_SIZE] = _T("\0");
	ULONG ulRead = __DEFAULT_BUF_SIZE;
	::memset(szt, 0x00, __DEFAULT_BUF_SIZE * sizeof(TCHAR));

	if ( !::HttpQueryInfo(m_hRequest,
	                      HTTP_QUERY_CONTENT_LENGTH,
	                      szt,
	                      reinterpret_cast<ULONG*>(&ulRead),
	                      NULL) )
	{
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, _T("query content-length failed..."));

		return 0;
	}

	return _ttol(szt);
}

LPCTSTR  W3Client::QueryContentType()
{
	static TCHAR szt[__DEFAULT_BUF_SIZE] = _T("\0");
	ULONG ulRead = __DEFAULT_BUF_SIZE;
	::memset(szt, 0x00, __DEFAULT_BUF_SIZE * sizeof(TCHAR));

	if ( !::HttpQueryInfo(m_hRequest, HTTP_QUERY_CONTENT_TYPE , szt, reinterpret_cast<ULONG*>(&ulRead), NULL) )
	{
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, _T("query content-type failed..."));

		return NULL;
	}

	return const_cast<TCHAR*>(szt);
}

unsigned int W3Client::QueryResult()
{
	TCHAR szt[__DEFAULT_BUF_SIZE] = _T("\0");
	ULONG ulRead = __DEFAULT_BUF_SIZE;
	::memset(szt, 0x00, __DEFAULT_BUF_SIZE * sizeof(TCHAR));

	if ( !::HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE , szt, reinterpret_cast<ULONG*>(&ulRead), NULL) )
	{
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, _T("query status code failed..."));
		return 404;
	}

	return _ttoi(szt);
}


ULONG W3Client::QueryRawHeader(BYTE* pBuf, ULONG ulLen)
{
	if ( !::HttpQueryInfo(m_hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pBuf, &ulLen, NULL) )
	{
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, _T("query cookie failed..."));
		return 0;
	}

	return ulLen;
}

ULONG W3Client::Response(BYTE* pBuf, ULONG ulLen)
{
	ULONG ulRead = 0;

	try
	{
		if ( !m_hRequest )
		{
			throw _T("connection failed...");
		}

		if ( !::InternetReadFile(m_hRequest, pBuf, ulLen, &ulRead) )
		{
			throw _T("response failed...");
		}

	}
	catch ( LPCTSTR szm )
	{
		::InternetCloseHandle(m_hRequest);
		m_hRequest = NULL;
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, szm);
	}
	catch (...)
	{
		::InternetCloseHandle(m_hRequest);
		m_hRequest = NULL;
		__w3cexcept(m_strAddress, m_wPort, m_eType, m_strUri, _T("unknown exception..."));
	}

	return ulRead;
}

bool W3Client::GetFile(LPCTSTR lpstrUri, LPCTSTR szfile, bool bAscii /*=false*/)
{
	bool bResult = true;

	try
	{
		if ( !m_hOpen || !m_hConnection )
		{
			throw _T("connection failed");
		}

		m_hRequest =::FtpOpenFile(m_hConnection,
		                          lpstrUri,
		                          GENERIC_READ,
		                          (bAscii ? INTERNET_FLAG_TRANSFER_ASCII : INTERNET_FLAG_TRANSFER_BINARY),
		                          NULL);

		if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
		{
			throw _T("request failed...");
		}

		HANDLE hFile =::CreateFile(szfile, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

		BYTE buf[__DEFAULT_BUF_SIZE] = "\0";
		ULONG nread = 0;

		if (hFile)
		{
			while (::InternetReadFile(m_hRequest, buf, __DEFAULT_BUF_SIZE, &nread) && nread > 0)
			{
				::WriteFile(hFile, buf, nread, &nread, NULL);
			}

			::CloseHandle(hFile);
		}
	}
	catch (LPCTSTR szm)
	{
		bResult = false;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, szm);
	}
	catch (...)
	{
		bResult = false;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, _T("unknown exception..."));
	}

	::InternetCloseHandle(m_hRequest);
	m_hRequest = NULL;

	return bResult;
}

ULONG W3Client::GetFile(LPCTSTR lpstrUri, BYTE* pBuf, ULONG ulLen, bool bAscii /*=false*/)
{
	ULONG ulRead = 0;

	try
	{
		if (!m_hOpen || !m_hConnection)
		{
			throw _T("connection failed");
		}

		if (!m_hRequest)
		{
			m_hRequest =::FtpOpenFile(m_hConnection,
			                          lpstrUri,
			                          GENERIC_READ,
			                          (bAscii ? INTERNET_FLAG_TRANSFER_ASCII : INTERNET_FLAG_TRANSFER_BINARY),
			                          NULL);
		}

		if (!m_hRequest || m_hRequest == INVALID_HANDLE_VALUE)
		{
			throw _T("request failed...");
		}

		if (!::InternetReadFile(m_hRequest, pBuf, ulLen, &ulRead) || ulRead <= 0)
		{
			::InternetCloseHandle(m_hRequest);
			m_hRequest = NULL;
		}
	}
	catch (LPCTSTR szm)
	{
		ulRead = 0;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, szm);
	}
	catch (...)
	{
		ulRead = 0;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, _T("unknown exception..."));
	}

	return ulRead;
}

bool W3Client::PutFile(LPCTSTR lpstrUri, LPCTSTR szfile, bool bAscii /*=false*/)
{
	bool bResult = true;

	try
	{
		if ( !m_hOpen || !m_hConnection )
		{
			throw _T("connection failed");
		}

		m_hRequest =::FtpOpenFile(m_hConnection,
		                          lpstrUri,
		                          GENERIC_WRITE,
		                          (bAscii ? INTERNET_FLAG_TRANSFER_ASCII : INTERNET_FLAG_TRANSFER_BINARY),
		                          NULL);

		if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
		{
			throw _T("request failed...");
		}

		HANDLE hFile =::CreateFile(szfile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

		BYTE byBuf[__DEFAULT_BUF_SIZE] = "\0";
		ULONG ulRead = 0;

		if ( hFile )
		{
			while (::ReadFile(hFile, byBuf, __DEFAULT_BUF_SIZE, &ulRead, 0) && ulRead > 0)
				::InternetWriteFile(m_hRequest, byBuf, ulRead, &ulRead);

			::CloseHandle(hFile);
		}

	}
	catch ( LPCTSTR szm )
	{
		bResult = false;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, szm);
	}
	catch (...)
	{
		bResult = false;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, _T("unknown exception..."));
	}

	::InternetCloseHandle(m_hRequest);
	m_hRequest = NULL;

	return bResult;
}

ULONG W3Client::PutFile(LPCTSTR lpstrUri, BYTE* pBuf, ULONG /*len*/, bool bAscii)
{
	ULONG ulRead = 0;

	try
	{
		if (!m_hOpen || !m_hConnection)
		{
			throw _T("connection failed");
		}

		if (!m_hRequest)
		{
			m_hRequest =::FtpOpenFile(m_hConnection,
			                          lpstrUri,
			                          GENERIC_WRITE,
			                          (bAscii ? INTERNET_FLAG_TRANSFER_ASCII : INTERNET_FLAG_TRANSFER_BINARY),
			                          NULL);
		}

		if (!m_hRequest || m_hRequest == INVALID_HANDLE_VALUE)
		{
			throw _T("request failed...");
		}

		if (!::InternetWriteFile(m_hRequest, pBuf, ulRead, &ulRead) || ulRead <= 0)
		{
			::InternetCloseHandle(m_hRequest);
			m_hRequest = NULL;
		}
	}
	catch (LPCTSTR szm)
	{
		ulRead = 0;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, szm);
	}
	catch (...)
	{
		ulRead = 0;
		__w3cexcept(m_strAddress, m_wPort, m_eType, lpstrUri, _T("unknown exception..."));
	}

	return ulRead;
}



void __w3curlparse(LPCTSTR lpstrUrl,
                   TCHAR* pszProtocol, size_t sProtocol,
                   TCHAR* pszUser, size_t sUser,
                   TCHAR* pszPwd, size_t sPwd,
                   TCHAR* pszAddress, size_t sAddr,
                   TCHAR* pszUri, size_t sUri,
                   WORD& wPort)
{
	ULONG ulPos = 0;
	bool bflag = false;

	while ( _tcslen(lpstrUrl) > 0 && ulPos < _tcslen(lpstrUrl) && _tcsnccmp((lpstrUrl + ulPos), _T(":"), 1) )
	{
		++ulPos;
	}

	if ( !_tcsnccmp((lpstrUrl + ulPos + 1), _T("/"), 1) ) 	// is protocol
	{
		if (pszProtocol)
		{
			_tcsncpy_s(pszProtocol, sProtocol, lpstrUrl, ulPos);
			pszProtocol[ulPos] = 0;
		}

		bflag = true;
	}
	else 	// is host
	{
		if ( pszProtocol )
		{
			_tcsncpy_s(pszProtocol, sProtocol, _T("http"), 4);
			pszProtocol[5] = _T('\0');
		}
	}

	ULONG nsp = 0, usp = 0;

	if ( bflag )
	{
		usp = nsp = ulPos += 3;
	}
	else
	{
		usp = nsp = ulPos = 0;
	}

	while ( _tcslen(lpstrUrl) > 0 && usp < _tcslen(lpstrUrl) && _tcsnccmp((lpstrUrl + usp), _T("@"), 1) )
	{
		++usp;
	}

	if ( usp < _tcslen(lpstrUrl) )  // find username and find password
	{
		ULONG ssp = nsp;

		while ( _tcslen(lpstrUrl) > 0 && ulPos < _tcslen(lpstrUrl) && _tcsnccmp((lpstrUrl + ssp), _T(":"), 1) )
		{
			++ssp;
		}

		if ( ssp < usp ) // find
		{
			_tcsncpy_s(pszUser, sUser, lpstrUrl + nsp, ssp - nsp);
			pszUser[ssp-nsp+1] = _T('\0');
			_tcsncpy_s(pszPwd, sPwd, lpstrUrl + ssp + 1, usp - ssp - 1);
			pszPwd[usp-ssp] = _T('\0');
		}

		nsp = ulPos = usp + 1;
	}

	bflag = false;

	while ( _tcslen(lpstrUrl) > 0 && ulPos < _tcslen(lpstrUrl) && _tcsnccmp((lpstrUrl + ulPos), _T("/"), 1) )
	{
		++ulPos;
	}

	ULONG nf = nsp;

	for ( ; nf <= ulPos; ++nf )
	{
		if ( !_tcsnccmp((lpstrUrl + nf), _T(":"), 1) )  // find PORT
		{
			bflag = true;
			break;
		}
	}

	if ( bflag )
	{
		TCHAR sztmp[1024] = _T("\0");
		_tcsncpy_s(sztmp, 1024, (lpstrUrl + nf + 1), ulPos - nf);
		wPort = (WORD)(_ttol(sztmp));
		_tcsncpy_s(pszAddress, sAddr, (lpstrUrl + nsp), nf - nsp);
	}
	else if ( !_tcscmp(pszProtocol, _T("https")) )
	{
		wPort = INTERNET_DEFAULT_HTTPS_PORT;
		_tcsncpy_s(pszAddress, sAddr, (lpstrUrl + nsp), ulPos - nsp);
	}
	else if ( !_tcscmp(pszProtocol, _T("ftp")) )
	{
		wPort = INTERNET_DEFAULT_FTP_PORT;
		_tcsncpy_s(pszAddress, sAddr, (lpstrUrl + nsp), ulPos - nsp);
	}
	else
	{
		wPort = INTERNET_DEFAULT_HTTP_PORT;
		_tcsncpy_s(pszAddress, sAddr, (lpstrUrl + nsp), ulPos - nsp);
	}

	if ( ulPos < _tcslen(lpstrUrl) )  // find URI
	{
		_tcsncpy_s(pszUri, sUri, (lpstrUrl + ulPos), _tcslen(lpstrUrl) - ulPos);
	}
	else
	{
		pszUri[0] = _T('/');
		pszUri[1] = _T('\0');
	}

	return;
}

AsyncW3Client::AsyncW3Client()
		: W3Client(), m_hCompleteRequestEvent(NULL)
{}

bool AsyncW3Client::Request(LPCTSTR lpstrUri, w3m m/* = reqGet*/, LPCTSTR lpstrRef/* = NULL*/)
{
	m_hCompleteRequestEvent =::CreateEvent(NULL, FALSE, FALSE, NULL);

	return W3Client::Request(lpstrUri, m, lpstrRef);
}

ULONG AsyncW3Client::Response(BYTE* pBuf, ULONG ulLen)
{
	::CloseHandle(m_hCompleteRequestEvent);
	m_hCompleteRequestEvent = NULL;

	return W3Client::Response(pBuf, ulLen);
}

#pragma warning ( disable : 4701 )

// Asynchronized www client
bool AsyncW3Client::Connect(LPCTSTR szaddress,
                            INTERNET_STATUS_CALLBACK lpfn,
                            LPCTSTR lpstrUser /*=NULL*/,
                            LPCTSTR lpstrPassword /*=NULL*/,
                            LPCTSTR lpstrAgent /*=__W3_DEFAULT_AGENT*/)
{

	TCHAR szProtocol[__DEFAULT_BUF_SIZE] = _T("\0");
	TCHAR szus[__DEFAULT_BUF_SIZE] = _T("\0");
	TCHAR szpw[__DEFAULT_BUF_SIZE] = _T("\0");
	TCHAR sza[__DEFAULT_BUF_SIZE] = _T("\0");
	TCHAR szuri[__DEFAULT_BUF_SIZE] = _T("\0");
	WORD wPort = 0;

	__w3curlparse(szaddress,
	              szProtocol, __DEFAULT_BUF_SIZE,
	              szus, __DEFAULT_BUF_SIZE,
	              szpw, __DEFAULT_BUF_SIZE,
	              sza, __DEFAULT_BUF_SIZE,
	              szuri, __DEFAULT_BUF_SIZE,
	              wPort);

	w3t wt;

	if ( !_tcsnccmp(szProtocol, _T("https"), 5) )
	{
		wt = w3https;
	}
	else if ( !_tcsnccmp(szProtocol, _T("http"), 4) )
	{
		wt = w3http;
	}
	else if ( !_tcsnccmp(szProtocol, _T("ftp"), 3) )
	{
		wt = w3ftp;
	}

	m_strUri = szuri;

	return Connect(sza, wPort,
	               lpfn,
	               ((lpstrUser || (!lpstrUser && _tcslen(szus) == 0)) ? lpstrUser : szus),
	               ((lpstrPassword || (!lpstrPassword && _tcslen(szpw) == 0)) ? lpstrPassword : szpw),
	               wt, lpstrAgent);
}

#pragma warning ( default : 4701 )

bool AsyncW3Client::Connect(LPCTSTR lpstrAddress,
                            WORD wPort,
                            INTERNET_STATUS_CALLBACK lpfn,
                            LPCTSTR lpstrUser /*=NULL*/,
                            LPCTSTR lpstrPassword /*=NULL*/,
                            w3t eType /*=w3http*/,
                            LPCTSTR lpstrAgent /*=__W3_DEFAULT_AGENT*/)
{
	bool bResult = true;

	try
	{
		m_hOpen =::InternetOpen(lpstrAgent, // agent
		                        INTERNET_OPEN_TYPE_PRECONFIG, // access type
		                        NULL, // proxy
		                        NULL, // proxy by pass
		                        INTERNET_FLAG_ASYNC); // flags

		if ( !m_hOpen || INVALID_HANDLE_VALUE == m_hOpen )
		{
			throw _T("open internet failed...");
		}

		/*INTERNET_STATUS_CALLBACK pc =*/
		::InternetSetStatusCallback(m_hOpen, lpfn);

		switch ( eType )
		{
		case w3ftp:
			m_hConnection =::InternetConnect(m_hOpen,
			                                 lpstrAddress,
			                                 (INTERNET_PORT)(wPort ? wPort : INTERNET_DEFAULT_FTP_PORT),
			                                 lpstrUser, lpstrPassword,
			                                 INTERNET_SERVICE_FTP,
			                                 INTERNET_FLAG_PASSIVE,
			                                 reinterpret_cast<ULONG>(this));
			break;
		case w3http:
			m_hConnection =::InternetConnect(m_hOpen,
			                                 lpstrAddress,
			                                 (INTERNET_PORT)(wPort ? wPort : INTERNET_DEFAULT_HTTP_PORT),
			                                 lpstrUser, lpstrPassword,
			                                 INTERNET_SERVICE_HTTP,
			                                 0,
			                                 reinterpret_cast<ULONG>(this));
			break;
		case w3https:
			m_hConnection =::InternetConnect(m_hOpen,
			                                 lpstrAddress,
			                                 (INTERNET_PORT)(wPort ? wPort : INTERNET_DEFAULT_HTTPS_PORT),
			                                 lpstrUser, lpstrPassword,
			                                 INTERNET_SERVICE_HTTP,
			                                 0,
			                                 reinterpret_cast<ULONG>(this));
			break;
		}

		if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
		{
			throw _T("connect failed...");
		}

		if ( ::InternetAttemptConnect(NULL) != ERROR_SUCCESS )
		{
			throw _T("connect failed...");
		}

		m_strAddress = lpstrAddress;

		if ( !wPort )
		{
			switch ( eType )
			{
			case w3ftp:
				m_wPort = INTERNET_DEFAULT_FTP_PORT;
				break;
			case w3http:
				m_wPort = INTERNET_DEFAULT_HTTP_PORT;
				break;
			case w3https:
				m_wPort = INTERNET_DEFAULT_HTTPS_PORT;
				break;
			}
		}
		else
		{
			m_wPort = wPort;
		}

		m_eType = eType;

		if ( lpstrUser )
		{
			m_strUser = lpstrUser;
		}

		if ( lpstrPassword )
		{
			m_strPassword = lpstrPassword;
		}

		InitializeCookies();
		InitializePostArguments();

	}
	catch ( LPCTSTR szm )
	{
		bResult = false;

		if ( m_hOpen || m_hOpen != INVALID_HANDLE_VALUE )
		{
			::InternetCloseHandle(m_hOpen);
		}

		if ( m_hConnection || m_hConnection != INVALID_HANDLE_VALUE )
		{
			::InternetCloseHandle(m_hConnection);
		}

		__w3cexcept(lpstrAddress, wPort, eType, szm);
	}
	catch ( ... )
	{
		bResult = false;

		if ( m_hOpen || m_hOpen != INVALID_HANDLE_VALUE )
		{
			::CloseHandle(m_hOpen);
		}

		if ( m_hConnection || m_hConnection != INVALID_HANDLE_VALUE )
		{
			::CloseHandle(m_hConnection);
		}

		__w3cexcept(lpstrAddress, wPort, eType, _T("unknown exception..."));
	}

	return bResult;
}

bool AsyncW3Client::RequestGet(LPCTSTR lpstrUrl, LPCTSTR lpstrRef /*=NULL*/)
{
	static LPCTSTR szAcceptType = __HTTP_ACCEPT_TYPE;

	if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
	{
		throw _T("handle not opened...");
	}

	m_hRequest =::HttpOpenRequest(m_hConnection,
	                              __HTTP_VERB_GET, // HTTP Verb
	                              lpstrUrl, // Object Name
	                              HTTP_VERSION, // Version
	                              lpstrRef, // Reference
	                              &szAcceptType, // Accept Type
	                              INTERNET_FLAG_NO_CACHE_WRITE | (m_eType == w3https ? INTERNET_FLAG_SECURE : 0),
	                              reinterpret_cast<ULONG>(this)); // context call-back point

	if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
	{
		throw _T("request failed...");
	}

	// REPLACE HEADER
	if ( !::HttpAddRequestHeaders(m_hRequest,
	                              __HTTP_ACCEPT, _tcslen(__HTTP_ACCEPT),
	                              HTTP_ADDREQ_FLAG_REPLACE) )
	{
		throw _T("additional header failed...");
	}

	// COOKIE ADD
	if ( 0 < m_listCookies.size() )
	{
		CString strUrl;

		switch ( m_eType )
		{
		case w3http:
			strUrl = _T("http://");
			break;
		case w3https:
			strUrl = _T("https://");
			break;
		}

		strUrl += m_strAddress;

		if ( !((w3http == m_eType && INTERNET_DEFAULT_HTTP_PORT == m_wPort)
		        || (w3https == m_eType && INTERNET_DEFAULT_HTTPS_PORT == m_wPort)) )
		{
			TCHAR tmp[10] = _T("\0");
			_stprintf(tmp, _T(":%d"), m_wPort);
			strUrl += tmp;
		}

		strUrl += lpstrUrl;

		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin(); it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* pc = reinterpret_cast<HTTP_COOKIE*>(*it);

			if ( !::InternetSetCookie(strUrl, pc->strName, pc->strValue) )
			{
				throw _T("add cookie failed...");
			}
		}
	}

	// SEND REQUEST
	if ( !::HttpSendRequest(m_hRequest,	// handle by returned HttpOpenRequest
	                        NULL, // additional HTTP header
	                        0, // additional HTTP header length
	                        NULL, // additional data in HTTP Post or HTTP Put
	                        0)// additional data length
	        && ::GetLastError() != ERROR_IO_PENDING )
	{
		throw _T("asynchronized request failed...");
	}

	return true;
}

bool AsyncW3Client::RequestPost(LPCTSTR lpstrUri, LPCTSTR lpstrRef /*=NULL*/)
{
	static LPCTSTR szAcceptType = __HTTP_ACCEPT_TYPE;
	static LPCTSTR szContentType = _T("Content-Type: application/x-www-form-urlencoded\r\n");

	BYTE* pBuf = NULL;
	ULONG ulLen = 0;

	if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
	{
		throw _T("handle not opened...");
	}


	m_hRequest =::HttpOpenRequest(m_hConnection,
	                              __HTTP_VERB_POST, // HTTP Verb
	                              lpstrUri, // Object Name
	                              HTTP_VERSION, // Version
	                              lpstrRef, // Reference
	                              &szAcceptType, // Accept Type
	                              INTERNET_FLAG_KEEP_CONNECTION |
	                              INTERNET_FLAG_NO_CACHE_WRITE |
	                              INTERNET_FLAG_FORMS_SUBMIT |
	                              (m_eType == w3https ? INTERNET_FLAG_SECURE : 0),
	                              reinterpret_cast<ULONG>(this)); // context call-back point

	if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
	{
		throw _T("request failed...");
	}

	// REPLACE HEADER
	if ( !::HttpAddRequestHeaders( m_hRequest, __HTTP_ACCEPT, _tcslen(__HTTP_ACCEPT), HTTP_ADDREQ_FLAG_REPLACE) )
	{
		throw _T("additional header failed...");
	}

	// COOKIE ADD
	if ( m_listCookies.size() > 0 )
	{
		CString szurl;

		switch (m_eType)
		{
		case w3http:
			szurl = _T("http://");
			break;
		case w3https:
			szurl = _T("https://");
			break;
		}

		szurl += m_strAddress;

		if ( !((m_eType == w3http && m_wPort == INTERNET_DEFAULT_HTTP_PORT)
		        || (m_eType == w3https && m_wPort == INTERNET_DEFAULT_HTTPS_PORT)) )
		{
			TCHAR tmp[10] = _T("\0");
			_stprintf(tmp, _T(":%d"), m_wPort);
			szurl += tmp;
		}

		szurl += lpstrUri;

		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin(); it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* pc = reinterpret_cast<HTTP_COOKIE*>(*it);

			if (!::InternetSetCookie(szurl, pc->strName, pc->strValue))
			{
				throw _T("add cookie failed...");
			}
		}
	}

	// GET POST ARGUMENTS
	pBuf = reinterpret_cast<BYTE*>(::HeapAlloc(::GetProcessHeap(),
	                               HEAP_ZERO_MEMORY,
	                               GetPostArgumentsLength() + 1));
	ulLen = GetPostData(pBuf, GetPostArgumentsLength() + 1);

	// SEND REQUEST WITH HttpSendRequestEx and InternetWriteFile
	static INTERNET_BUFFERS InternetBufferIn = {0};
	InternetBufferIn.dwStructSize = sizeof(INTERNET_BUFFERS);
	InternetBufferIn.Next = NULL;
	InternetBufferIn.lpcszHeader = szContentType;
	InternetBufferIn.dwHeadersLength = _tcslen(szContentType);
	InternetBufferIn.lpvBuffer = pBuf;
	InternetBufferIn.dwBufferLength = ulLen;

	if ( !::HttpSendRequestEx(m_hRequest,
	                          &InternetBufferIn,
	                          NULL, HSR_INITIATE,
	                          reinterpret_cast<ULONG>(this))
	        && ::GetLastError() != ERROR_IO_PENDING )
	{
		// free
		::HeapFree(::GetProcessHeap(), 0, pBuf);
		throw _T("request failed");
	}

	WaitCompleteRequest();

	if ( !::HttpEndRequest(m_hRequest,
	                       NULL,
	                       HSR_ASYNC | HSR_INITIATE,
	                       reinterpret_cast<ULONG>(this))
	        && ::GetLastError() != ERROR_IO_PENDING )
	{
		// free
		::HeapFree(::GetProcessHeap(), 0, pBuf);
		throw _T("request failed");
	}

	::HeapFree(::GetProcessHeap(), 0, pBuf);

	return true;
}

bool AsyncW3Client::RequestPost2(LPCTSTR lpstrUri, LPCTSTR lpstrRef /*=NULL*/)
{
	static LPCTSTR szAcceptType = __HTTP_ACCEPT_TYPE;
	static LPCTSTR szContentType = _T("Content-Type: multipart/form-data; boundary=--MULTI-PARTS-FORM-DATA-BOUNDARY\r\n");

	BYTE* pBuf = NULL;
	ULONG ulLen = 0;

	if ( !m_hConnection || m_hConnection == INVALID_HANDLE_VALUE )
	{
		throw _T("handle not opened...");
	}


	m_hRequest =::HttpOpenRequest( m_hConnection,
	                               __HTTP_VERB_POST, // HTTP Verb
	                               lpstrUri, // Object Name
	                               HTTP_VERSION, // Version
	                               lpstrRef, // Reference
	                               &szAcceptType, // Accept Type
	                               INTERNET_FLAG_KEEP_CONNECTION |
	                               INTERNET_FLAG_NO_CACHE_WRITE |
	                               INTERNET_FLAG_FORMS_SUBMIT |
	                               (m_eType == w3https ? INTERNET_FLAG_SECURE : 0),
	                               reinterpret_cast<ULONG>(this)); // context call-back point

	if ( !m_hRequest || m_hRequest == INVALID_HANDLE_VALUE )
	{
		throw _T("request failed...");
	}

	// REPLACE HEADER
	if ( !::HttpAddRequestHeaders( m_hRequest, __HTTP_ACCEPT, _tcslen(__HTTP_ACCEPT), HTTP_ADDREQ_FLAG_REPLACE) )
	{
		throw _T("additional header failed...");
	}

	if ( !::HttpAddRequestHeaders( m_hRequest, szContentType, _tcslen(szContentType), HTTP_ADDREQ_FLAG_ADD_IF_NEW) )
	{
		throw _T("additional header failed...");
	}

	// COOKIE ADD
	if ( m_listCookies.size() > 0 )
	{
		CString strUrl;

		switch ( m_eType )
		{
		case w3http:
			strUrl = _T("http://");
			break;
		case w3https:
			strUrl = _T("https://");
			break;
		}

		strUrl += m_strAddress;

		if ( !( (m_eType == w3http && m_wPort == INTERNET_DEFAULT_HTTP_PORT) ||
		        (m_eType == w3https && m_wPort == INTERNET_DEFAULT_HTTPS_PORT) ) )
		{
			TCHAR tmp[10] = _T("\0");
			_stprintf(tmp, _T(":%d"), m_wPort);
			strUrl += tmp;
		}

		strUrl += lpstrUri;

		for ( std::list<HTTP_COOKIE*>::iterator it = m_listCookies.begin(); it != m_listCookies.end(); ++it )
		{
			HTTP_COOKIE* pc = reinterpret_cast<HTTP_COOKIE*>(*it);

			if ( !::InternetSetCookie(strUrl, pc->strName, pc->strValue) )
			{
				throw _T("add cookie failed...");
			}
		}
	}

	// build multi-parts/form-data
	ulLen = AllocMultiPartsFormData(pBuf, "--MULTI-PARTS-FORM-DATA-BOUNDARY");

	// ADD HEADER CONTENT LENGTH
	TCHAR szcl[__DEFAULT_BUF_SIZE] = _T("\0");
	_stprintf(szcl, _T("Content-Length: %d\r\n"), ulLen);

	if ( !::HttpAddRequestHeaders(m_hRequest, szcl, _tcslen(szcl), HTTP_ADDREQ_FLAG_ADD_IF_NEW) )
	{
		throw _T("additional header failed...");
	}

	// SEND REQUEST WITH HttpSendRequestEx and InternetWriteFile
	static INTERNET_BUFFERS InternetBufferIn = {0};
	InternetBufferIn.dwStructSize = sizeof(INTERNET_BUFFERS);
	InternetBufferIn.Next = NULL;
	InternetBufferIn.lpvBuffer = pBuf;
	InternetBufferIn.dwBufferLength = ulLen;

	if ( !::HttpSendRequestEx(m_hRequest,
	                          &InternetBufferIn,
	                          NULL,
	                          HSR_INITIATE,
	                          reinterpret_cast<ULONG>(this))
	        && ::GetLastError() != ERROR_IO_PENDING )
	{
		// free
		FreeMultiPartsFormData(pBuf);
		throw _T("request failed");
	}

	WaitCompleteRequest();

	if ( !::HttpEndRequest(m_hRequest,
	                       NULL,
	                       HSR_ASYNC | HSR_INITIATE,
	                       reinterpret_cast<ULONG>(this))
	        && ::GetLastError() != ERROR_IO_PENDING )
	{
		// free
		FreeMultiPartsFormData(pBuf);
		throw _T("request failed");
	}

	// free multi-parts/form-data
	FreeMultiPartsFormData(pBuf);

	return true;
}

void AsyncW3Client::SetCompleteRequest()
{
	::SetEvent(m_hCompleteRequestEvent);
}

bool AsyncW3Client::WaitCompleteRequest(ULONG ntime /*=INFINITE*/)
{
	if ( !m_hCompleteRequestEvent || m_hCompleteRequestEvent == INVALID_HANDLE_VALUE )
	{
		return false;
	}

	return ::WaitForSingleObject(m_hCompleteRequestEvent, ntime) == WAIT_OBJECT_0 ? true : false;
}

